package Controle;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.zip.Adler32;
import java.util.zip.CheckedInputStream;

import IHM.IHM;
import Metier.Page;
import Metier.Projet;

/**
 * Classe de gestion de l'IHM et du Metier
 * Permet de faire le lien entre les deux packages
 * @author Aubry Thomas
 * @author Bouchet Sylvain
 * @author Fauvel Alexis
 * @author Lessieur Jocelyn
 * @author Mathias Bastien 
 * @author Teuf Antoine
 * @version 1.0
 */
public class Controleur implements Serializable{

	/**
	 * Permet de contenir l'IHM
	 */
	private IHM ihm;
	
	/**
	 * ArrayList contenant la liste des projets
	 */
	private ArrayList<Projet> alPro;
	
	/**
	 * Index du projet en cours
	 */
	private int projetEnCours;
	
	/**
	 * Fichier de sauvegarde des projets
	 */
	private File save= new File(System.getProperty("user.home")+"/GenerateurSiteWeb/.save.dat");
	
	/**
	 * Chemin d'acces aux projets
	 */
	private File root;
	
	/**
	 * Permet de contenir une page, afin d'effectuer des copier/coller
	 */
	private Page pressePapier;

	/**
	 * Constructeur de la classe Controleur,permet d'initialiser l'IHM et de lancer l'application 
	 */
	public Controleur() {
		// A la première utilisation on crée le dossier Projets ou seront
		// contenu tous les projets créés par le programme
		root = new File(System.getProperty("user.home")+"/GenerateurSiteWeb/Projets");
		if (!root.exists())
			root.mkdirs();

		// Recherche d'un fichier de sauvegarde, chargement si existe, sinon
		// creation
		if (save.exists()) {
			try {

				ObjectInputStream chargement = new ObjectInputStream(
						new FileInputStream(save));
				alPro = (ArrayList<Projet>) chargement.readObject();
				chargement.close();
			} catch (Exception e) {
				System.out.println(e);
			}
		} else {
			alPro = new ArrayList<Projet>(); // Seul ligne a garder, le reste
												// que pr les tests.
		}

		projetEnCours = -1;

		ihm = new IHM(this);
		ihm.setInfo("Emplacement du fichier de sauvegarde : "+save.toURI().toString());
		//initialise le pressePapier a null
		pressePapier=null;
	}
	
	/**
	 * Permet de recuperer le chemin d'acces au projet
	 * @return	le chemin absolu vers le dossier "/GenerateurSiteWeb/Projets"
	 */
	public File getRacine(){
		return root;
	}

	/**
	 * Permet de recuperer le projet en cours
	 * @return	une instance de Projet contenant le projet en cours
	 */
	public Projet getProjetEnCours() {
		if (projetEnCours<0 || projetEnCours>=alPro.size()|| alPro.isEmpty())
			return null;
		else
			return alPro.get(projetEnCours);
	}

	/**
	 * Permet de recuperer la liste des projets
	 * @return	une Arraylist de Projet contenant tout les projets
	 */
	public ArrayList<Projet> getProjets() {
		return alPro;
	}

	/**
	 * Permet la creation d'un nouveau projet
	 * @param nom		nom du projet à creer
	 * @param auteur	l'auteur du projet
	 */
	public void creationProjet(String nom, String auteur) {
		alPro.add(new Projet(this, nom, auteur));
	}

	/**
	 * Permet de recuperer un projet par son nom
	 * @param nom	nom du projet à recuperer
	 * @return	une instance de Projet dont le nom est passé en parametre 
	 */
	public Projet getProjetByNom(String nom) {
		for (Projet projet : alPro)
			if (projet.getNom().equals(nom))
				return projet;
		return getProjetEnCours();
	}

	/**
	 * Permet de supprimer un projet par son nom
	 * @param nom	nom du projet à supprimer
	 */
	public void removeProjetByName(String nom) {
		alPro.remove(getProjetByNom(nom));
	}

	/**
	 * Permet de remplacer le projet en cours par un autre projet
	 * @param projet	projet à mettre en "projet en cours"
	 */
	public void setProjetEnCours(Projet projet) {
		for (int i = 0; i < alPro.size(); i++)
			if (alPro.get(i).equals(projet))
				projetEnCours = i;
	}

	/**
	 * Permet de recuperer l'index dans l'arraylist du projet en cours
	 * @return l'index dans l'arraylist du projet en cours
	 */
	public int getNumProjetEnCours() {
		return projetEnCours;
	}

	/**
	 * Permet d'effectuer une sauvegarde des projets en cours
	 */
	public void sauvegarde() {
		try {
			ObjectOutputStream sauvegarde = new ObjectOutputStream(
					new FileOutputStream(save));
			sauvegarde.writeObject(alPro);
			sauvegarde.close();
			ihm.setInfo("Sauvegarde effectuée !");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Vérifie s'il y a eu des changements dans les projets (pour proposer ou non la sauvegarde en quittant) 
	 * @return vrai(true) si un changement à été fait sinon faux(false)
	 */
	public boolean verifChangement() {
		boolean retour = true;
		File temp = new File("temp.dat");

		if (save.exists()) {
			try {
				ObjectOutputStream sauvegarde = new ObjectOutputStream(
						new FileOutputStream(temp));
				sauvegarde.writeObject(alPro);
				sauvegarde.close();
			} catch (Exception e) {
				System.out.println(e);
			}
			
			if(calculChecksum(temp) == calculChecksum(save))
				retour = false;
		}

		System.gc();
		temp.delete();

		return retour;
	}

	/**
	 * 
	 * Méthode utilisé pour comparer deux fichiers (dans verifChangement())
	 * @param adresse_fichier	fichier dont le checksum doit etre calculé
	 * @return	un nombre representant le Checksum
	 */
	public long calculChecksum(File adresse_fichier) {
		try {
			FileInputStream fis = new FileInputStream(adresse_fichier);
			CheckedInputStream cis = new CheckedInputStream(fis, new Adler32());
			byte[] tempBuf = new byte[128];
			while (cis.read(tempBuf) >= 0) {
			}
			long checksum = cis.getChecksum().getValue();
			return checksum;
		} catch (IOException e) {
			return 0;
		}
	}
	
	/**
	 * Permet de recuperer la page copier dans le pressePapier
	 * @return	une instance de Page, contenant la page du presse papier
	 */
	public Page getPressePapier(){
		return pressePapier;
	}
	
	/**
	 * Permet de remplacer la page dans le pressePapier
	 * @param p	Page à mettre dans le presse papier
	 */
	public void setPressePapier(Page p){
		pressePapier=p;
	}
	
	/**
	 * Permet de copier un repertoire vers une destination
	 * @param source		chemin du repertoire à copier
	 * @param destination	Chemin du dossier de destination
	 */
	public void copierRep(File source, File destination) {
		if(source.isFile())
			copierFichier(source,destination);
		else {
			File[] ssReps = source.listFiles();
			for (File ssRep : ssReps) {
				copierRep(ssRep, new File(destination.toString()+"/"+ssRep.getName()));
			}
		}
	}
	
	/**
	 * Permet de copier un fichier vers une destination
	 * @param source		fichier à copier
	 * @param destination	chemin du dossier de destination
	 */
	public void copierFichier(File source, File destination) {
		if(!destination.getParentFile().exists())
			destination.getParentFile().mkdirs();
		
		try{
			// Declaration et ouverture des flux
			java.io.FileInputStream sourceFile = new java.io.FileInputStream(source);
			
			try{
				java.io.FileOutputStream destinationFile = null;
				
				try{
					destinationFile = new FileOutputStream(destination);
					
					// Lecture par segment de 0.5Mo 
					byte buffer[] = new byte[512 * 1024];
					int nbLecture;
					
					while ((nbLecture = sourceFile.read(buffer)) != -1){
						destinationFile.write(buffer, 0, nbLecture);
					}
				} finally {
					destinationFile.close();
				}
			} finally {
				sourceFile.close();
			}
		} catch (IOException e){
			e.printStackTrace();
		}
	}
	
	/**
	 * Permet d'exporter projet
	 * @param nomProjet		nom du projet à exporter
	 * @param destination	chemin de destination de l'exportation
	 */
	public void exporterProjet(String nomProjet, File destination) {
		
		getProjetByNom(nomProjet).genererTout();
		copierRep(new File(getRacine().toString()+"/"+nomProjet),new File(destination.toString()+"/"+nomProjet));
		
		try {
			File export = new File(destination.toString()+"/"+nomProjet+"/export.gsw");
			ObjectOutputStream exportProjet = new ObjectOutputStream(new FileOutputStream(export));
			exportProjet.writeObject(getProjetByNom(nomProjet));
			exportProjet.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Permet d'importer un projet
	 * @param rep	chemin du projet à importer
	 * @return		vrai(true) si le projet a ete importer sinon faux(false)
	 */
	public boolean importerProjet(File rep) {
		boolean retour = true;
		
		//Ajout du Projet dans l'AL grace au fichier serialisé :
		File fichierExport = new File(rep.toString()+"/export.gsw");
		if(!fichierExport.exists())
			return false;
		
		File verif=new File(root.getPath()+"/"+rep.getName());
		int i=1;
		String nom=rep.getName();
		if(verif.exists()){
			if(nom.matches("^.*\\([1-9]\\)$"))
				nom=nom.substring(0,nom.length()-3)+"("+i+")";
			else if(nom.matches("^.*\\([1-9][0-9]\\)$"))
				nom=nom.substring(0,nom.length()-4)+"("+i+")";
			else
				nom=rep.getName()+"("+i+")";
			
			verif=new File(root.getPath()+"/"+nom);
			while(verif.exists()){
				i++;
				if(i>10)
					nom=nom.substring(0,nom.length()-4)+"("+i+")";
				else
					nom=nom.substring(0,nom.length()-3)+"("+i+")";
				verif=new File(root.getPath()+"/"+nom);
			}
			
		}
		
		
		
		try {
			ObjectInputStream importProjet = new ObjectInputStream(
					new FileInputStream(fichierExport));
			Projet p = (Projet) importProjet.readObject();
			p.setNom(nom);
			for(Page pa:p.getPages()){
				pa.setProjet(p);
			}
			
			alPro.add(p);
			importProjet.close();
		} catch (Exception e) {
			System.out.println(e);
		}
		
		//Copie du repertoire dans l'arborescence du generateur :
		copierRep(rep, verif);
		
		//Suppression du fichier d'export dans l'arbo du generateur :
		fichierExport = new File(verif.getPath()+"/export.gsw");
		fichierExport.delete();
		
		
		
		return retour;
	}

	public static void main(String[] args) {
		new Controleur();
	}
}
